/* =======================================================================
* jQuery.responsive-nav.js v1.0.39
* https://github.com/viljamis/responsive-nav.js
* http://responsive-nav.com
*
* Copyright (c) 2015 @viljamis
* Available under the MIT license
 * ======================================================================== */
/* global Event */
(function(document, window, index) {
  // Index is used to keep multiple navs on the same page namespaced
  "use strict";
  var responsiveNav = function(el, options) {
    var computed = !!window.getComputedStyle;

    /**
    * getComputedStyle polyfill for old browsers
    */
    if (!computed) {
      window.getComputedStyle = function(el) {
        this.el = el;
        this.getPropertyValue = function(prop) {
          var re = /(\-([a-z]){1})/g;
          if (prop === "float") {
            prop = "styleFloat";
          }
          if (re.test(prop)) {
            prop = prop.replace(re,
            function() {
              return arguments[2].toUpperCase();
            });
          }
          return el.currentStyle[prop] ? el.currentStyle[prop] : null;
        };
        return this;
      };
    }
    /* exported addEvent, removeEvent, getChildren, setAttributes, addClass, removeClass, forEach */

    /**
    * Add Event
    * fn arg can be an object or a function, thanks to handleEvent
    * read more at: http://www.thecssninja.com/javascript/handleevent
    *
    * @param  {element}  element
    * @param  {event}    event
    * @param  {Function} fn
    * @param  {boolean}  bubbling
    */
    var addEvent = function(el, evt, fn, bubble) {
      if ("addEventListener" in el) {
        // BBOS6 doesn't support handleEvent, catch and polyfill
        try {
          el.addEventListener(evt, fn, bubble);
        } catch(e) {
          if (typeof fn === "object" && fn.handleEvent) {
            el.addEventListener(evt,
            function(e) {
              // Bind fn as this and set first arg as event object
              fn.handleEvent.call(fn, e);
            },
            bubble);
          } else {
            throw e;
          }
        }
      } else if ("attachEvent" in el) {
        // check if the callback is an object and contains handleEvent
        if (typeof fn === "object" && fn.handleEvent) {
          el.attachEvent("on" + evt,
          function() {
            // Bind fn as this
            fn.handleEvent.call(fn);
          });
        } else {
          el.attachEvent("on" + evt, fn);
        }
      }
    },

    /**
  * Remove Event
  *
  * @param  {element}  element
  * @param  {event}    event
  * @param  {Function} fn
  * @param  {boolean}  bubbling
  */
    removeEvent = function(el, evt, fn, bubble) {
      if ("removeEventListener" in el) {
        try {
          el.removeEventListener(evt, fn, bubble);
        } catch(e) {
          if (typeof fn === "object" && fn.handleEvent) {
            el.removeEventListener(evt,
            function(e) {
              fn.handleEvent.call(fn, e);
            },
            bubble);
          } else {
            throw e;
          }
        }
      } else if ("detachEvent" in el) {
        if (typeof fn === "object" && fn.handleEvent) {
          el.detachEvent("on" + evt,
          function() {
            fn.handleEvent.call(fn);
          });
        } else {
          el.detachEvent("on" + evt, fn);
        }
      }
    },

    /**
  * Get the children of any element
  *
  * @param  {element}
  * @return {array} Returns matching elements in an array
  */
    getChildren = function(e) {
      if (e.children.length < 1) {
        throw new Error("The Nav container has no containing elements");
      }
      // Store all children in array
      var children = [];
      // Loop through children and store in array if child != TextNode
      for (var i = 0; i < e.children.length; i++) {
        if (e.children[i].nodeType === 1) {
          children.push(e.children[i]);
        }
      }
      return children;
    },

    /**
  * Sets multiple attributes at once
  *
  * @param {element} element
  * @param {attrs}   attrs
  */
    setAttributes = function(el, attrs) {
      for (var key in attrs) {
        el.setAttribute(key, attrs[key]);
      }
    },

    /**
  * Adds a class to any element
  *
  * @param {element} element
  * @param {string}  class
  */
    addClass = function(el, cls) {
      if (el.className.indexOf(cls) !== 0) {
        el.className += " " + cls;
        el.className = el.className.replace(/(^\s*)|(\s*$)/g, "");
      }
    },

    /**
  * Remove a class from any element
  *
  * @param  {element} element
  * @param  {string}  class
  */
    removeClass = function(el, cls) {
      var reg = new RegExp("(\\s|^)" + cls + "(\\s|$)");
      el.className = el.className.replace(reg, " ").replace(/(^\s*)|(\s*$)/g, "");
    },

    /**
  * forEach method that passes back the stuff we need
  *
  * @param  {array}    array
  * @param  {Function} callback
  * @param  {scope}    scope
  */
    forEach = function(array, callback, scope) {
      for (var i = 0; i < array.length; i++) {
        callback.call(scope, i, array[i]);
      }
    };

    var nav, opts, navToggle, styleElement = document.createElement("style"),
    htmlEl = document.documentElement,
    hasAnimFinished,
    isMobile,
    navOpen;

    var ResponsiveNav = function(el, options) {
      var i;

      /**
    * Default options
    * @type {Object}
    */
      this.options = {
        animate: true,
        // Boolean: Use CSS3 transitions, true or false
        transition: 284,
        // Integer: Speed of the transition, in milliseconds
        label: "Menu",
        // String: Label for the navigation toggle
        insert: "before",
        // String: Insert the toggle before or after the navigation
        customToggle: "",
        // Selector: Specify the ID of a custom toggle
        closeOnNavClick: false,
        // Boolean: Close the navigation when one of the links are clicked
        openPos: "relative",
        // String: Position of the opened nav, relative or static
        navClass: "nav-collapse",
        // String: Default CSS class. If changed, you need to edit the CSS too!
        navActiveClass: "js-nav-active",
        // String: Class that is added to <html> element when nav is active
        jsClass: "js",
        // String: 'JS enabled' class which is added to <html> element
        init: function() {},
        // Function: Init callback
        open: function() {},
        // Function: Open callback
        close: function() {} // Function: Close callback
      };

      // User defined options
      for (i in options) {
        this.options[i] = options[i];
      }

      // Adds "js" class for <html>
      addClass(htmlEl, this.options.jsClass);

      // Wrapper
      this.wrapperEl = el.replace("#", "");

      // Try selecting ID first
      if (document.getElementById(this.wrapperEl)) {
        this.wrapper = document.getElementById(this.wrapperEl);

        // If element with an ID doesn't exist, use querySelector
      } else if (document.querySelector(this.wrapperEl)) {
        this.wrapper = document.querySelector(this.wrapperEl);

        // If element doesn't exists, stop here.
      } else {
        throw new Error("The nav element you are trying to select doesn't exist");
      }

      // Inner wrapper
      this.wrapper.inner = getChildren(this.wrapper);

      // For minification
      opts = this.options;
      nav = this.wrapper;

      // Init
      this._init(this);
    };

    ResponsiveNav.prototype = {

      /**
    * Unattaches events and removes any classes that were added
    */
      destroy: function() {
        this._removeStyles();
        removeClass(nav, "closed");
        removeClass(nav, "opened");
        removeClass(nav, opts.navClass);
        removeClass(nav, opts.navClass + "-" + this.index);
        removeClass(htmlEl, opts.navActiveClass);
        nav.removeAttribute("style");
        nav.removeAttribute("aria-hidden");

        removeEvent(window, "resize", this, false);
        removeEvent(window, "focus", this, false);
        removeEvent(document.body, "touchmove", this, false);
        removeEvent(navToggle, "touchstart", this, false);
        removeEvent(navToggle, "touchend", this, false);
        removeEvent(navToggle, "mouseup", this, false);
        removeEvent(navToggle, "keyup", this, false);
        removeEvent(navToggle, "click", this, false);

        if (!opts.customToggle) {
          navToggle.parentNode.removeChild(navToggle);
        } else {
          navToggle.removeAttribute("aria-hidden");
        }
      },

      /**
    * Toggles the navigation open/close
    */
      toggle: function() {
        if (hasAnimFinished === true) {
          if (!navOpen) {
            this.open();
          } else {
            this.close();
          }
        }
      },

      /**
    * Opens the navigation
    */
      open: function() {
        if (!navOpen) {
          removeClass(nav, "closed");
          addClass(nav, "opened");
          addClass(htmlEl, opts.navActiveClass);
          addClass(navToggle, "active");
          nav.style.position = opts.openPos;
          setAttributes(nav, {
            "aria-hidden": "false"
          });
          navOpen = true;
          opts.open();
        }
      },

      /**
    * Closes the navigation
    */
      close: function() {
        if (navOpen) {
          addClass(nav, "closed");
          removeClass(nav, "opened");
          removeClass(htmlEl, opts.navActiveClass);
          removeClass(navToggle, "active");
          setAttributes(nav, {
            "aria-hidden": "true"
          });

          // If animations are enabled, wait until they finish
          if (opts.animate) {
            hasAnimFinished = false;
            setTimeout(function() {
              nav.style.position = "absolute";
              hasAnimFinished = true;
            },
            opts.transition + 10);

            // Animations aren't enabled, we can do these immediately
          } else {
            nav.style.position = "absolute";
          }

          navOpen = false;
          opts.close();
        }
      },

      /**
    * Resize is called on window resize and orientation change.
    * It initializes the CSS styles and height calculations.
    */
      resize: function() {

        // Resize watches navigation toggle's display state
        if (window.getComputedStyle(navToggle, null).getPropertyValue("display") !== "none") {

          isMobile = true;
          setAttributes(navToggle, {
            "aria-hidden": "false"
          });

          // If the navigation is hidden
          if (nav.className.match(/(^|\s)closed(\s|$)/)) {
            setAttributes(nav, {
              "aria-hidden": "true"
            });
            nav.style.position = "absolute";
          }

          this._createStyles();
          this._calcHeight();
        } else {
          isMobile = false;
          setAttributes(navToggle, {
            "aria-hidden": "true"
          });
          setAttributes(nav, {
            "aria-hidden": "false"
          });
          nav.style.position = opts.openPos;
          this._removeStyles();
        }
      },

      /**
    * Takes care of all even handling
    *
    * @param  {event} event
    * @return {type} returns the type of event that should be used
    */
      handleEvent: function(e) {
        var evt = e || window.event;
        switch (evt.type) {
        case "touchstart":
          this._onTouchStart(evt);
          break;
        case "touchmove":
          this._onTouchMove(evt);
          break;
        case "touchend":
        case "mouseup":
          this._onTouchEnd(evt);
          break;
        case "click":
          this._preventDefault(evt);
          break;
        case "keyup":
          this._onKeyUp(evt);
          break;
        case "focus":
        case "resize":
          this.resize(evt);
          break;
        }
      },

      /**
    * Initializes the widget
    */
      _init: function() {
        this.index = index++;

        addClass(nav, opts.navClass);
        addClass(nav, opts.navClass + "-" + this.index);
        addClass(nav, "closed");
        hasAnimFinished = true;
        navOpen = false;

        this._closeOnNavClick();
        this._createToggle();
        this._transitions();
        this.resize();

        /**
      * On IE8 the resize event triggers too early for some reason
      * so it's called here again on init to make sure all the
      * calculated styles are correct.
      */
        var self = this;
        setTimeout(function() {
          self.resize();
        },
        20);

        addEvent(window, "resize", this, false);
        addEvent(window, "focus", this, false);
        addEvent(document.body, "touchmove", this, false);
        addEvent(navToggle, "touchstart", this, false);
        addEvent(navToggle, "touchend", this, false);
        addEvent(navToggle, "mouseup", this, false);
        addEvent(navToggle, "keyup", this, false);
        addEvent(navToggle, "click", this, false);

        /**
      * Init callback here
      */
        opts.init();
      },

      /**
    * Creates Styles to the <head>
    */
      _createStyles: function() {
        if (!styleElement.parentNode) {
          styleElement.type = "text/css";
          document.getElementsByTagName("head")[0].appendChild(styleElement);
        }
      },

      /**
    * Removes styles from the <head>
    */
      _removeStyles: function() {
        if (styleElement.parentNode) {
          styleElement.parentNode.removeChild(styleElement);
        }
      },

      /**
    * Creates Navigation Toggle
    */
      _createToggle: function() {
        // If there's no toggle, let's create one
        if (!opts.customToggle) {
          var toggle = document.createElement("a");
          toggle.innerHTML = opts.label;
          setAttributes(toggle, {
            "href": "#",
            "class": "Hui-nav-toggle"
          });

          // Determine where to insert the toggle
          if (opts.insert === "after") {
            nav.parentNode.insertBefore(toggle, nav.nextSibling);
          } else {
            nav.parentNode.insertBefore(toggle, nav);
          }

          navToggle = toggle;

          // There is a toggle already, let's use that one
        } else {
          var toggleEl = opts.customToggle.replace("#", "");

          if (document.getElementById(toggleEl)) {
            navToggle = document.getElementById(toggleEl);
          } else if (document.querySelector(toggleEl)) {
            navToggle = document.querySelector(toggleEl);
          } else {
            throw new Error("The custom nav toggle you are trying to select doesn't exist");
          }
        }
      },

      /**
    * Closes the navigation when a link inside is clicked.
    */
      _closeOnNavClick: function() {
        if (opts.closeOnNavClick) {
          var links = nav.getElementsByTagName("a"),
          self = this;
          forEach(links,
          function(i, el) {
            addEvent(links[i], "click",
            function() {
              if (isMobile) {
                self.toggle();
              }
            },
            false);
          });
        }
      },

      /**
    * Prevents the default functionality.
    *
    * @param  {event} event
    */
      _preventDefault: function(e) {
        if (e.preventDefault) {
          if (e.stopImmediatePropagation) {
            e.stopImmediatePropagation();
          }
          e.preventDefault();
          e.stopPropagation();
          return false;

          // This is strictly for old IE
        } else {
          e.returnValue = false;
        }
      },

      /**
    * On touch start we get the location of the touch.
    *
    * @param  {event} event
    */
      _onTouchStart: function(e) {
        if (!Event.prototype.stopImmediatePropagation) {
          this._preventDefault(e);
        }
        this.startX = e.touches[0].clientX;
        this.startY = e.touches[0].clientY;
        this.touchHasMoved = false;

        /**
      * Remove mouseup event completely here to avoid
      * double triggering the event.
      */
        removeEvent(navToggle, "mouseup", this, false);
      },

      /**
    * Check if the user is scrolling instead of tapping.
    *
    * @param  {event} event
    */
      _onTouchMove: function(e) {
        if (Math.abs(e.touches[0].clientX - this.startX) > 10 || Math.abs(e.touches[0].clientY - this.startY) > 10) {
          this.touchHasMoved = true;
        }
      },

      /**
    * On touch end toggle the navigation.
    *
    * @param  {event} event
    */
      _onTouchEnd: function(e) {
        this._preventDefault(e);
        if (!isMobile) {
          return;
        }

        // If the user isn't scrolling
        if (!this.touchHasMoved) {

          // If the event type is touch
          if (e.type === "touchend") {
            this.toggle();
            return;

            // Event type was click, not touch
          } else {
            var evt = e || window.event;

            // If it isn't a right click, do toggling
            if (! (evt.which === 3 || evt.button === 2)) {
              this.toggle();
            }
          }
        }
      },

      /**
    * For keyboard accessibility, toggle the navigation on Enter
    * keypress too.
    *
    * @param  {event} event
    */
      _onKeyUp: function(e) {
        var evt = e || window.event;
        if (evt.keyCode === 13) {
          this.toggle();
        }
      },

      /**
    * Adds the needed CSS transitions if animations are enabled
    */
      _transitions: function() {
        if (opts.animate) {
          var objStyle = nav.style,
          transition = "max-height " + opts.transition + "ms";

          objStyle.WebkitTransition = objStyle.MozTransition = objStyle.OTransition = objStyle.transition = transition;
        }
      },

      /**
    * Calculates the height of the navigation and then creates
    * styles which are later added to the page <head>
    */
      _calcHeight: function() {
        var savedHeight = 0;
        for (var i = 0; i < nav.inner.length; i++) {
          savedHeight += nav.inner[i].offsetHeight;
        }

        var innerStyles = "." + opts.jsClass + " ." + opts.navClass + "-" + this.index + ".opened{max-height:" + savedHeight + "px !important} ." + opts.jsClass + " ." + opts.navClass + "-" + this.index + ".opened.dropdown-active {max-height:9999px !important}";

        if (styleElement.styleSheet) {
          styleElement.styleSheet.cssText = innerStyles;
        } else {
          styleElement.innerHTML = innerStyles;
        }
        innerStyles = "";
      }
    };
    /**
    * Return new Responsive Nav
    */
    return new ResponsiveNav(el, options);
  };
  if (typeof module !== "undefined" && module.exports) {
    module.exports = responsiveNav;
  } else {
    window.responsiveNav = responsiveNav;
  }
} (document, window, 0));